import wx
from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin
from inspect import getsourcefile
import logging
from threading import Thread
from operator import attrgetter
from scipy import average

# The following is not working -- not a problem unless using py2app
# Set toolkit for traits  prior to importing
from enthought.etsconfig.api import ETSConfig
ETSConfig.toolkit = 'wx'
ETSConfig.enable_toolkit = 'wx'

from enthought.traits.ui.api import \
    View as TraitsView, \
    Item as TraitsItem, \
    Group as TraitsGroup
from enthought.traits.ui.menu import ApplyButton

# The recommended way to use wx with mpl is with the WXAgg backend
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import \
    FigureCanvasWxAgg as FigCanvas, \
    NavigationToolbar2WxAgg as NavigationToolbar
import pylab

from core import * 

# Define notification event for thread completion
EVT_RESULT_ID = wx.NewId()

def EVT_RESULT(win, func):
    """Define Result Event."""
    win.Connect(-1, -1, EVT_RESULT_ID, func)

# Simplify generation of bold text
BOLD_FONT = wx.Font(12, wx.SWISS, wx.NORMAL, wx.BOLD, False)

def bold_static_text(parent, text):
    tmp = wx.StaticText(parent, -1, text)
    tmp.SetFont(BOLD_FONT)
    return tmp
    
class ResultEvent(wx.PyEvent):
    """Simple event to carry arbitrary result data."""
    
    def __init__(self, data):
        """Init Result Event."""
        wx.PyEvent.__init__(self)
        self.SetEventType(EVT_RESULT_ID)
        self.data = data    

class LogToText(logging.Handler):
    
    def __init__(self, notify_window):
        logging.Handler.__init__(self)
        self._notify_window = notify_window
    
    def emit(self, record):
        wx.PostEvent(self._notify_window, ResultEvent(self.format(record)+'\n'))

class View(object):
    """A child view within a view selector."""
    
    def set_view_selector(self, view_selector):
        self.view_selector = view_selector
    
    def display(self):
        """Make this the currently chosen view in the view selector."""
        self.view_selector.show_view_obect(self)    

class ConsoleView(wx.TextCtrl,View):
    
    def __init__(self, parent, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize):
        wx.TextCtrl.__init__(self, parent, id=id, pos=pos, size=size, style=wx.TE_MULTILINE)
        self.SetEditable(False)
        self.SetBackgroundColour(wx.BLACK)
        self.SetStyle(0, -1, wx.TextAttr("white"))
        self.SetInsertionPoint(0)
        # Set up event handler for any worker thread results
        EVT_RESULT(self,self.on_console_result)

    def on_console_result(self, event):
        """Show Result status."""
        self.AppendText(event.data)

class PlotView(FigCanvas, View):
    def __init__(self, parent, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize):
        # initialize plot
        self.dpi = 100
        self.fig = Figure((3.0, 3.0), dpi=self.dpi)
        # initalize canvas
        FigCanvas.__init__(self, parent, id, self.fig)
    
    def clear(self):
        self.fig.clear()
        self.draw()

class AgentEditor(wx.Panel,View):
    
    def __init__(self, parent, agent_manager, id=-1, 
                 pos=wx.DefaultPosition, size=wx.DefaultSize):
        wx.Panel.__init__(self, parent, id, pos, size)
        self.Show(False)
        self.agent_manager = agent_manager
        self.currently_editing = None
                
        # editor panel (TODO: use wx.stc.StyledTextCtrl for syntax highlighting)
        self.editor = wx.TextCtrl(self, -1, style=wx.TE_MULTILINE | wx.TE_DONTWRAP)
        self.editor.SetEditable(True)
        self.editor.SetFont(wx.Font(12, wx.TELETYPE, wx.NORMAL, wx.NORMAL, False, face="Monaco"))
        self.editor.SetInsertionPoint(0)
        self.editor.Show(True)
        self.Bind(wx.EVT_TEXT, self.on_edit, self.editor)

        # header
        self.title_text = wx.StaticText(self,-1,"")
        
        # buttons
        self.save_button = wx.Button(self, -1, "Save")
        self.Bind(wx.EVT_BUTTON, self.on_save, self.save_button)
        self.close_button = wx.Button(self, -1, "Close")
        self.Bind(wx.EVT_BUTTON, self.on_close, self.close_button)
        self.hbox = wx.BoxSizer(wx.HORIZONTAL)
        self.hbox.Add(self.save_button)
        self.hbox.AddSpacer(10)
        self.hbox.Add(self.close_button)  
        self.hbox.AddSpacer(10)
        self.hbox.Add(self.title_text, 0, flag=wx.ALIGN_CENTER_VERTICAL)

        # layout
        self.vbox = wx.BoxSizer(wx.VERTICAL)
        self.vbox.Add(self.editor, 2, flag=wx.ALIGN_TOP|wx.EXPAND)
        self.vbox.Add(self.hbox, 0, flag=wx.ALIGN_TOP|wx.EXPAND|wx.BOTTOM|wx.TOP, border=2)
        self.SetSizer(self.vbox)
        self.vbox.Fit(self)
        
        self.clear()

    def on_edit(self,event):
        # check if is modified, if so, change state of save button
        if self.editor.IsModified():
            self.save_button.Enable()
    
    def on_save(self, event):
        if self.currently_editing != None:
            if self.editor.IsModified():
                # Save changes
                agent = self.agent_manager[self.currently_editing]
                path = getsourcefile(agent)
                f = open(path, 'w')
                f.write(self.editor.GetString(0,-1))
                f.close()
                self.editor.SetModified(False)
                self.save_button.Disable()
                # Reload
                try:
                    # dynamically load agent(s) from source code
                    loaded = self.agent_manager.load_file(path)
                    if len(loaded) == 0:
                        raise Exception("No agents found in file")
                    if len(loaded) > 1:
                        raise Exception("More than one agent selected")
                except Exception, e:
                    # display error message
                    wx.MessageDialog(self,message=str(e),caption="Error",style=wx.OK).ShowModal()
    
    def on_close(self, event=None):
        # check if we're in edit mode and there has been a change
        if self.currently_editing != None:
            if self.editor.IsModified():
                # Modified, so check if we should save changes
                message = 'Agent "%s" has been modified. Do you want to save changes?'%self.currently_editing
                answer = wx.MessageDialog(self,message=message,caption="Save changes?", 
                    style=wx.CANCEL | wx.YES_DEFAULT | wx.YES_NO).ShowModal()
                if answer == wx.ID_NO:
                    # edit the new agent
                    self.clear()
                elif answer == wx.ID_YES:
                    self.on_save(None)
                    self.clear()               
                elif answer == wx.ID_CANCEL:
                    # keep existing selection
                    pass
            else:
                # edit the new selection
                self.clear()

    def clear(self):
        self.currently_editing = None
        self.title_text.SetLabel('')
        self.editor.Clear()
        self.save_button.Disable()
        self.close_button.Disable()
        
    def edit_agent(self, agent_name):
        if agent_name:
            self.clear()
            self.currently_editing = agent_name
            agent = self.agent_manager[agent_name]
            # Set text in Editor
            f = open(getsourcefile(agent))
            self.editor.AppendText(f.read())
            f.close()
            self.editor.SetModified(False)
            self.close_button.Enable()
            # Change status
            self.title_text.SetLabel(getsourcefile(agent))
            self.title_text.SetFont(wx.Font(10, wx.SWISS, wx.ITALIC, wx.NORMAL, False))

class ViewSelector(wx.Panel):
    def __init__(self, parent, main, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize):
        wx.Panel.__init__(self, parent, id, pos, size)
        self.main = main
        self.views = []
        self.active_view = None
        
        # chooser
        self.view_choice = wx.Choice(self, -1)
        self.Bind(wx.EVT_CHOICE, self.on_change_view, self.view_choice)

        # layout
        self.vbox = wx.BoxSizer(wx.VERTICAL)
        self.vbox.Add(self.view_choice, 0, flag=wx.ALIGN_TOP|wx.BOTTOM|wx.EXPAND, border=1)
        self.SetSizer(self.vbox)
        self.vbox.Fit(self)

    def get_view(self, label):
        for x in self.views:
            if x['label'] == label:
                return x['view']
        return None

    def add_view(self, view, label, on_show_fn=None, on_hide_fn=None):
        assert(isinstance(view, View))
        # add the view
        self.views.append({'view':view,'label':label,'on_show_fn':on_show_fn,
                           'on_hide_fn':on_hide_fn})
        self.view_choice.Append(label)
        # bind this selector to it
        view.set_view_selector(self)
        # if no view is active, make the first view active
        if self.active_view == None:
            self.show_view(0)
        # otherwise, hide this view
        elif self.active_view != None:
            self.hide_view(len(self.views)-1)    
        
    def hide_view(self, index):
        if self.views[index]['on_hide_fn']:
            self.views[index]['on_hide_fn']()
        self.views[index]['view'].Show(False)
        if self.active_view == index:
            self.vbox.Detach(self.views[index]['view'])
            self.active_view = None
    
    def show_view(self, index):
        if self.views[index]['on_show_fn']:
            self.views[index]['on_show_fn']()
        self.views[index]['view'].Show(True)
        self.vbox.Add(self.views[index]['view'], 2, flag=wx.ALIGN_TOP|wx.EXPAND)
        self.active_view = index
        self.view_choice.SetStringSelection(self.views[index]['label'])

    def switch_to_view(self, index):
        assert(index < len(self.views))
        if index != self.active_view:
            self.hide_view(self.active_view)
            self.show_view(index)
            self.vbox.RecalcSizes()    

    def show_view_label(self, label):
        # get index of view
        for i in range(len(self.views)):
            if self.views[i]['label'] == label:
                break
        self.switch_to_view(i)

    def show_view_obect(self, view_object):
        # get index of view
        for i in range(len(self.views)):
            if self.views[i]['view'] == view_object:
                break
        self.switch_to_view(i)
   
    def on_change_view(self, event):
        # get index of view
        for i in range(len(self.views)):
            if self.views[i]['label'] == self.view_choice.GetStringSelection():
                break
        self.switch_to_view(i)  

class AutoWidthListCtrl(wx.ListCtrl, ListCtrlAutoWidthMixin):
    def __init__(self, parent, size):
        wx.ListCtrl.__init__(self, parent, -1, size=size, style=wx.LC_REPORT)
        ListCtrlAutoWidthMixin.__init__(self)

class AgentPopulationDialog(wx.Dialog):
    load_text = 'Load from file...'
    def __init__(self, parent, title, population, mode='INSERT', i=-1):
        assert(mode in ['INSERT', 'MODIFY'])
        wx.Dialog.__init__(self, parent, -1, title, size=(250, 210))
        self.pop = population
        self.mode = mode
        self.i = i
        # Agent type
        box1 = wx.BoxSizer(wx.HORIZONTAL)
        box1.Add(wx.StaticText(self, -1, 'Agent type:  '))
        choices=population.available_agents()+[self.load_text]
        self.choice = wx.Choice(self, -1, choices=choices)
        box1.Add(self.choice)
        if mode == 'MODIFY':
            default_val = population.get()[i]['class_name']
            self.choice.SetSelection(self.choice.FindString(default_val))
        self.Bind(wx.EVT_CHOICE, self.on_select_agent, self.choice)
        # Number of instances
        box2 = wx.BoxSizer(wx.HORIZONTAL)
        box2.Add(wx.StaticText(self, -1, 'Number of instances:  '))
        default_val = str(population.get()[i]['number']) if mode == 'MODIFY' else '1'
        self.number = wx.TextCtrl(self, -1, default_val, size=(50, -1))
        box2.Add(self.number)
        self.Bind(wx.EVT_TEXT, self.on_text_change, self.number)
        # Ok button
        box3 = wx.BoxSizer(wx.HORIZONTAL)
        ok_button = wx.Button(self, wx.ID_OK, 'OK', size=(70, 30))
        cancel_button = wx.Button(self, wx.ID_CANCEL, 'Cancel', size=(70, 30))
        box3.Add(ok_button, 1)
        box3.Add(cancel_button, 1, wx.LEFT, 5)
        self.Bind(wx.EVT_BUTTON, self.on_ok, ok_button)
        # Layout
        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(box1, 1, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 10)
        vbox.Add(box2, 1, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 10)
        vbox.Add(box3, 1, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 10)
        ok_button.SetDefault()
        self.SetSizer(vbox)

    def on_select_agent(self, event):
        """Load agent file, if necessary."""
        # check selection
        if self.choice.GetStringSelection() == self.load_text:
            file_dialog = wx.FileDialog(self,message="Select agent file",
                                        wildcard="*.py",style=wx.FD_OPEN)
            if file_dialog.ShowModal() == wx.ID_OK:
                loaded = self.pop.load_agent_file(file_dialog.GetPath())
                if len(loaded) > 0:
                    # reload choices
                    choices=self.pop.available_agents()+[self.load_text]
                    self.choice.SetItems(choices)
                    self.choice.SetStringSelection(loaded[0])
        
    def on_text_change(self, event):
        filtered = filter(lambda c:c.isdigit(), self.number.GetValue())
        self.number.SetValue(filtered)
        self.number.SetInsertionPointEnd()
        
    def on_ok(self, event):
        num = int(self.number.GetValue()) if len(self.number.GetValue()) > 0 else 1
        class_name = self.choice.GetStringSelection()
        if self.mode == 'MODIFY':
            self.pop.modify_number(self.i, num)
            self.pop.modify_class_name(self.i, class_name)
        elif self.mode == 'INSERT':
            self.pop.insert_entry(class_name, num, self.i)
        return self.EndModal(wx.ID_OK)
            
class AgentPopulationPanel(wx.Panel):
    "Panel corresponding to an AgentPopulation object"
    def __init__(self, parent, view_selector, agent_manager, agent_editor,
                id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize):
        wx.Panel.__init__(self, parent, id, pos, size)
        # create panel elements
        self.pop = AgentPopulation(agent_manager)
        self.pop.insert_entry('Random', 2)
        self.agent_editor = agent_editor
        self.lc = AutoWidthListCtrl(self, size=(200,120))
        self.lc.InsertColumn(0, 'Agent type', width=260)
        self.lc.InsertColumn(1, 'Instances')
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.on_change_selection, self.lc)
        self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.on_change_selection, self.lc)     
        self.add_button = wx.Button(self, -1, "Insert", style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.on_add, self.add_button)
        self.remove_button = wx.Button(self, -1, "Remove", style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.on_remove, self.remove_button)
        self.modify_button = wx.Button(self, -1, "Modify", style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.on_modify, self.modify_button)
        self.code_button = wx.Button(self, -1, "Source code", style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.on_edit_code, self.code_button)
        self.remove_button.Disable()
        self.modify_button.Disable()
        self.code_button.Disable()
        # layout
        self.vbox = wx.BoxSizer(wx.VERTICAL)
        self.vbox.Add(self.lc, 0, flag=wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.EXPAND , border=10)
        self.hbox = wx.BoxSizer(wx.HORIZONTAL)
        self.hbox.Add(self.add_button, 0, flag=wx.BOTTOM, border=1)
        self.hbox.Add(self.remove_button, 0)
        self.hbox.Add(self.modify_button, 0)
        self.hbox.Add(self.code_button, 0)
        self.vbox.Add(self.hbox, 0, flag=wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.EXPAND , border=10)        
        self.SetSizer(self.vbox)
        self.vbox.Fit(self)
        self.refresh_view()
    
    def on_change_selection(self, event):
        new_sel_i =  self.lc.GetFirstSelected()
        # close existing editor
        self.agent_editor.on_close(None)
        # button enable/disable
        if new_sel_i >= 0:
            self.remove_button.Enable()
            self.modify_button.Enable()
            self.code_button.Enable()
        else:
            self.remove_button.Disable()
            self.modify_button.Disable()
            self.code_button.Disable()            

    def on_add(self, event):
        i = self.lc.GetFirstSelected()
        apd = AgentPopulationDialog(self, 'Add agent', self.pop, 'INSERT', i)        
        if apd.ShowModal() == wx.ID_OK:
            self.refresh_view(i+1)
        apd.Destroy()
    
    def on_remove(self, event):
        self.pop.remove_entry(self.lc.GetFirstSelected())
        self.refresh_view()
    
    def on_modify(self, event):
        i = self.lc.GetFirstSelected()
        if i > -1:
            apd = AgentPopulationDialog(self, 'Modify agent', self.pop, 'MODIFY', i)        
            apd.ShowModal()
            apd.Destroy()
        self.refresh_view()

    def on_edit_code(self, event):
        # Display the Editor Window
        self.agent_editor.display()
        # Set text in Editor
        class_name = self.pop.get()[self.lc.GetFirstSelected()]['class_name']
        self.agent_editor.edit_agent(class_name)

    def refresh_view(self, focus_on=-1):
        focused = self.lc.GetFirstSelected()
        self.lc.DeleteAllItems()
        for i, x in enumerate(self.pop.get()):
            self.lc.InsertStringItem(i, x['class_name'])
            self.lc.SetStringItem(i,1,str(x['number']))
        # gray out buttons if box is empty
        if len(self.pop.get()) == 0:
            self.remove_button.Disable()
            self.modify_button.Disable()
            self.code_button.Disable()
        # select appropriate row
        else:
            if focus_on > 0:
                self.lc.Select(focus_on)
            else:
                self.lc.Select(focused)

    def get_agentpop(self):
        return self.pop

class AgentSetPanel(wx.Panel):
    "Panel corresponding to an AgentSet object"

    def __init__(self, parent, view_selector, agent_manager, agent_editor, id=-1, 
                pos=wx.DefaultPosition, size=wx.DefaultSize):
        wx.Panel.__init__(self, parent, id, pos, size)
        # create panel elements
        self.agents = agent_manager
        self.agent_editor = agent_editor
        self.agent_box = wx.CheckListBox(self, -1,choices=self.agents.names(),size=(180,150))
        self.Bind(wx.EVT_LISTBOX, self.on_agent_box, self.agent_box)        
        self.agent_box.SetCheckedStrings(self.agents.names())
        self.import_agent_button = wx.Button(self, -1, "Import")
        self.Bind(wx.EVT_BUTTON, self.on_import_agent, self.import_agent_button)
        # layout
        self.vbox = wx.BoxSizer(wx.VERTICAL)
        self.vbox.Add(self.agent_box, 0, flag=wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.EXPAND , border=10)
        self.vbox.Add(self.import_agent_button, 0, flag=wx.ALIGN_LEFT | wx.LEFT | wx.BOTTOM, border=10)
        self.SetSizer(self.vbox)
        self.vbox.Fit(self)
    
    def get_agentset(self):
        return AgentSet([self.agents[x] for x in self.agent_box.GetCheckedStrings()])
        
    def on_agent_box(self, event):
        new_sel_i =  self.agent_box.GetSelection()
        # close existing editor
        self.agent_editor.on_close(None)
        self.agent_box.SetSelection(new_sel_i)
        # open new agent, if it is a string
        if self.agent_box.GetStringSelection():
            # Display the Editor Window
            self.agent_editor.display()
            # Set text in Editor
            self.agent_editor.edit_agent(self.agent_box.GetStringSelection())

    def on_import_agent(self, event):
        file_dialog = wx.FileDialog(self,message="Select agent file",wildcard="*.py",style=wx.FD_OPEN)
        if file_dialog.ShowModal() == wx.ID_OK:
            path = file_dialog.GetPath()
            try:
                orig = set(self.agents.names())
                # dynamically load agent(s) from source code
                loaded = self.agents.load_file(path)
                # load into checklistbox
                checked = self.agent_box.GetCheckedStrings()
                self.agent_box.SetItems(self.agents.names())
                self.agent_box.SetCheckedStrings(list(checked) + loaded)
                # display status message
                if len(loaded) == 0:
                    message = "No Agents found in file."
                    caption = "Error"
                elif len(loaded) == 1:
                    if orig & set(loaded):
                        message = "Agent %s re-imported."%loaded[0]
                        caption = "Success"
                    else:
                        message = "Agent %s imported."%loaded[0]
                        caption = "Success"                        
                else:
                    message = "The following agents were imported (or re-imported): %s."%', '.join(loaded) 
                    caption = "Success"
                wx.MessageDialog(self,message=message,caption=caption,style=wx.OK).ShowModal()
            except Exception, e:
                # display error message
                wx.MessageDialog(self,message=str(e),caption="Error",style=wx.OK).ShowModal()

class GameThread(Thread):
    
    def __init__(self, game_ui):
        Thread.__init__(self)
        self.game_ui = game_ui
        self.complete = False
        
    def run(self):
        assert(self.game_ui)
        self.game_ui.game.play()
        self.game_ui.draw_plot()
        self.complete = True
        self.game_ui.run_button.SetValue(False)
        
    def isComplete(self):
        return self.complete
        
class GameUI(wx.Panel):
    def __init__(self, parent, view_selector, agent_manager, agent_editor, plot,
                id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize):
        wx.Panel.__init__(self, parent, id, pos, size)
        self.view_selector = view_selector
        self.thread = None
        self.plot = plot
        self.game = None
        # agent controls
        self.agentpop_panel = AgentPopulationPanel(self, view_selector, agent_manager, agent_editor)
        # world controls
        self.world = World()
        view = TraitsView(TraitsGroup(
                          TraitsItem(name = 'cost_of_investing'), 
                          TraitsItem(name = 'cost_of_negevent'), 
                          TraitsItem(name = 'p'),
                          TraitsItem(name = 'graph_type'),
                          TraitsItem(name = 'num_rounds'),
                          show_border=True))
        self.world_view = self.world.edit_traits(view=view,
                                                 parent=self,
                                                 kind='subpanel').control        
        # simulator controls
        self.run_button = wx.ToggleButton(self, -1, "Run Game")
        self.run_button.SetFont(BOLD_FONT)
        self.Bind(wx.EVT_TOGGLEBUTTON, self.on_run, self.run_button)
        # layout
        self.vbox = wx.BoxSizer(wx.VERTICAL)
        self.vbox.Add(bold_static_text(self,"Agent Population"), 0, flag=wx.ALIGN_LEFT | wx.LEFT | wx.TOP, border=10)
        self.vbox.Add(self.agentpop_panel, 0, flag=wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.EXPAND , border=0)
        self.vbox.Add(bold_static_text(self,"World"), 0, flag=wx.ALIGN_LEFT | wx.LEFT | wx.TOP, border=10)
        self.vbox.Add(self.world_view, 0, flag=wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.EXPAND, border=10)
        self.vbox.AddSpacer(10)
        self.vbox.Add(self.run_button, 0, flag=wx.ALIGN_LEFT | wx.ALL | wx.EXPAND, border=10)        
        self.SetSizer(self.vbox)
        self.vbox.Fit(self)              
                
    def on_run(self, event):
        # Select the Console Window
        self.view_selector.show_view_label('Console')
        # Check if running
        if self.thread == None or self.thread.isComplete():
            # Validate population size
            if self.agentpop_panel.get_agentpop().population_size() < 2:
                self.run_button.SetValue(False)
                message='The population must contain at least 2 agent instances'
                wx.MessageDialog(self, message, caption="Error", style=wx.OK).ShowModal()
            else:
                self.view_selector.get_view('Console').Clear()
                self.view_selector.get_view('Plot').clear()
                # Run
                agents = self.agentpop_panel.get_agentpop().make_agent_instances()
                self.game = Game(agents, self.world, True)
                self.thread = GameThread(self)
                self.thread.start()
                self.run_button.SetValue(True)
        elif self.thread.isAlive():
            self.run_button.SetValue(True)

    def draw_plot(self):
        assert(self.game != None)
        
        fig = self.plot.fig
        
        fig.clear()
        
        # ave outcome plot
        ave_outcomes = [average([i['outcome'] for i in r]) for r in self.game.H]
        axes = fig.add_subplot(311)
        axes.set_axis_bgcolor('black')
        axes.set_title('Average outcome', size=10)
        axes.plot(ave_outcomes, linewidth=2)
        pylab.xlabel("round",fontsize=10)
        pylab.ylabel("ave outcome")
        pylab.setp(axes.get_xticklabels(), fontsize=8)
        pylab.setp(axes.get_yticklabels(), fontsize=8)
        
        # negevents plot
        ave_negevent = [average([i['negevent'] for i in r]) for r in self.game.H]
        axes = fig.add_subplot(312)
        axes.set_axis_bgcolor('black')
        axes.set_title('Negative events', size=10)
        axes.plot(ave_negevent, linewidth=2)
        pylab.xlabel("round",fontsize=10)
        pylab.ylabel("% neg events")
        pylab.setp(axes.get_xticklabels(), fontsize=8)
        pylab.setp(axes.get_yticklabels(), fontsize=8)

        # % invest plot
        ave_investing = [average([i['invested'] for i in r]) for r in self.game.H]
        axes = fig.add_subplot(313)
        axes.set_axis_bgcolor('black')
        axes.set_title('Percent of agents investing', size=10)
        axes.plot(ave_investing, linewidth=2)
        pylab.xlabel("round",fontsize=10)
        pylab.ylabel("% investing")                
        pylab.setp(axes.get_xticklabels(), fontsize=8)
        pylab.setp(axes.get_yticklabels(), fontsize=8)

        fig.subplots_adjust(hspace=.45)
        
        self.plot.draw()
        
        self.view_selector.show_view_label('Plot')

class TournamentThread(Thread):
    
    def __init__(self, tourn_ui, agentset, world):
        Thread.__init__(self)
        self.tourn = Tournament(agentset, world)
        self.tourn_ui = tourn_ui
        self.complete = False
        
    def run(self):
        assert(self.tourn_ui)
        self.tourn.run()
        self.complete = True
        self.tourn_ui.run_button.SetValue(False)

    def isComplete(self):
        return self.complete
        
class TournamentUI(wx.Panel):
    def __init__(self, parent, view_selector, agent_manager, agent_editor, plot,
                id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize):
        wx.Panel.__init__(self, parent, id, pos, size)
        self.view_selector = view_selector
        self.thread = None
        # agent controls
        self.agentset_panel = AgentSetPanel(self, view_selector, agent_manager, agent_editor)
        # world controls
        self.world = World()
        view = TraitsView(TraitsGroup(
                          TraitsItem(name = 'cost_of_investing'), 
                          TraitsItem(name = 'cost_of_negevent'), 
                          TraitsItem(name = 'p'),
                          #TraitsItem(name = 'graph_type'),
                          TraitsItem(name = 'num_rounds'),
                          show_border=True))
        self.world_view = self.world.edit_traits(view=view,
                                                 parent=self,
                                                 kind='subpanel').control        
        # simulator controls
        self.run_button = wx.ToggleButton(self, -1, "Run Tournament")
        self.run_button.SetFont(BOLD_FONT)
        self.Bind(wx.EVT_TOGGLEBUTTON, self.on_run, self.run_button)
        # layout
        self.vbox = wx.BoxSizer(wx.VERTICAL)
        self.vbox.Add(bold_static_text(self,"Agent Set"), 0, flag=wx.ALIGN_LEFT | wx.LEFT | wx.TOP, border=10)
        self.vbox.Add(self.agentset_panel, 0, flag=wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.EXPAND , border=0)
        self.vbox.Add(bold_static_text(self,"World"), 0, flag=wx.ALIGN_LEFT | wx.LEFT | wx.TOP, border=10)
        self.vbox.Add(self.world_view, 0, flag=wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.EXPAND, border=10)
        self.vbox.AddSpacer(10)
        self.vbox.Add(self.run_button, 0, flag=wx.ALIGN_LEFT | wx.ALL | wx.EXPAND, border=10)        
        self.SetSizer(self.vbox)
        self.vbox.Fit(self)              
                
    def on_run(self, event):
        # Select the Console Window
        self.view_selector.show_view_label('Console')
        # Check if already running
        if self.thread == None or self.thread.isComplete():
            # Validate agent set size
            if self.agentset_panel.get_agentset().size() < 2:
                self.run_button.SetValue(False)
                message='The agent set must contain at least 2 agent classes'
                wx.MessageDialog(self, message, caption="Error", style=wx.OK).ShowModal()
            else:            
                self.view_selector.get_view('Console').Clear()
                self.view_selector.get_view('Plot').clear()
                self.thread = TournamentThread(self,self.agentset_panel.get_agentset(), self.world)
                self.thread.start()
                self.run_button.SetValue(True)
        elif self.thread.isAlive():
            self.run_button.SetValue(True)


class MainFrame(wx.Frame):
    title = 'IDS Simulator'
    vsize = 500
    hsize = 900
    hsplit = .45*hsize
    def __init__(self):
        # create frame and set size
        wx.Frame.__init__(self, None, -1, self.title, size=wx.Size(self.hsize,self.vsize))
        self.SetSizeHints(self.hsize,self.vsize,-1,-1)
        # split the window
        self.splitter = wx.SplitterWindow(self, -1, style=wx.SP_NOBORDER)
        self.splitter.SetMinimumPaneSize(self.hsplit)
        # viz panel
        self.vizpanel = ViewSelector(self.splitter, self, -1)
        self.agent_manager = AgentManager()
        self.agent_editor = AgentEditor(self.vizpanel, self.agent_manager, -1)
        self.console = ConsoleView(self.vizpanel, -1)
        self.plot = PlotView(self.vizpanel, -1)
        self.vizpanel.add_view(self.console, 'Console')
        self.vizpanel.add_view(self.agent_editor, 'Agent Editor', None, self.agent_editor.on_close)
        self.vizpanel.add_view(self.plot, 'Plot')
        # uipanel is a notebook
        self.uipanel = wx.Notebook(self.splitter)
        page1 = GameUI(self.uipanel, self.vizpanel, self.agent_manager, self.agent_editor, self.plot)
        page2 = TournamentUI(self.uipanel, self.vizpanel, self.agent_manager, self.agent_editor, self.plot)
        self.uipanel.AddPage(page1, "Game")
        self.uipanel.AddPage(page2, "Tournament")
        self.splitter.SplitVertically(self.uipanel, self.vizpanel, self.hsplit)        
        # add a status bar
        #self.CreateStatusBar()

class IDSGui(wx.App):
    def OnInit(self):
        frame = MainFrame()
        # logging
        logger=logging.getLogger()
        logger.setLevel(logging.DEBUG)
        handler = LogToText(frame.console)
        formatter = logging.Formatter('%(message)s')
        handler.setFormatter(formatter)
        logger.addHandler(handler)
        frame.Show(True)
        self.SetTopWindow(frame)        
        return True

if __name__ == '__main__':
    app = IDSGui()
    app.MainLoop()
